﻿using System;
using System.Threading;
using System.Web;
using System.Web.Caching;

namespace BigTestV3.common
{
    public class TimeHelper:IDisposable
    {
         /// <summary>
        /// 初始化 Timer 类的新实例，使用 TimeSpan 值来度量时间间隔。
        /// </summary>
        /// <param name="callback">一个 TimerCallback 委托，表示要执行的方法。 </param>
        /// <param name="state">一个包含回调方法要使用的信息的对象，或者为 空引用（在 Visual Basic 中为 Nothing）。 </param>
        /// <param name="dueTime">TimeSpan，表示在 callback 参数调用它的方法之前延迟的时间量。指定 -1 毫秒以防止启动计时器。指定零 (0) 以立即启动计时器。 </param>
        /// <param name="period">在调用 callback 所引用的方法之间的时间间隔。指定 -1 毫秒可以禁用定期终止。 </param>
        /// <param name="timerID">定时器标识符，不能重复</param>
        /// <remarks>
        /// 如果 dueTime 为零 (0)，则立即调用 callback。如果 dueTime 是 -1 毫秒，则不会调用 callback；计时器将被禁用，但通过调用 Change 方法可以重新启用计时器。
        /// 如果 period 为零 (0) 或 -1 毫秒，而且 dueTime 为正，则只会调用一次 callback；计时器的定期行为将被禁用，但通过使用 Change 方法可以重新启用该行为。
        /// 为 callback 指定的方法应是可重入的，这是因为 ThreadPool 线程会调用该方法。该方法在以下两种情况下可以同时在两个线程池线程中执行：一是计时器间隔小于执行该方法所需的时间；二是所有线程池线程都在使用，并且多次对该方法进行排队。
        /// </remarks>
        public TimeHelper(TimerCallback callback, Object state, TimeSpan dueTime, TimeSpan period, string timerID)
        {
            _callback = callback;
            _state = state;
            _dueTime = dueTime;
            _period = period;
            _cacheID = timerID;
            Run(dueTime);
        }

        /// <summary>
        /// 初始化 Timer 类的新实例，使用 TimeSpan 值来度量时间间隔， 建议使用WebTimer(TimerCallback callback, Object state, TimeSpan dueTime, TimeSpan period, string timerID)，避免造成更改定时器操作的时候，旧操作仍旧在运行
        /// </summary>
        /// <param name="callback">一个 TimerCallback 委托，表示要执行的方法。 </param>
        /// <param name="state">一个包含回调方法要使用的信息的对象，或者为 空引用（在 Visual Basic 中为 Nothing）。 </param>
        /// <param name="dueTime">TimeSpan，表示在 callback 参数调用它的方法之前延迟的时间量。指定 -1 毫秒以防止启动计时器。指定零 (0) 以立即启动计时器。 </param>
        /// <param name="period">在调用 callback 所引用的方法之间的时间间隔。指定 -1 毫秒可以禁用定期终止。 </param>
        /// <param name="timerID">定时器标识符，重复的话会取消之前的任务</param>
        /// <remarks>
        /// 如果 dueTime 为零 (0)，则立即调用 callback。如果 dueTime 是 -1 毫秒，则不会调用 callback；计时器将被禁用，但通过调用 Change 方法可以重新启用计时器。
        /// 如果 period 为零 (0) 或 -1 毫秒，而且 dueTime 为正，则只会调用一次 callback；计时器的定期行为将被禁用，但通过使用 Change 方法可以重新启用该行为。
        /// 为 callback 指定的方法应是可重入的，这是因为 ThreadPool 线程会调用该方法。该方法在以下两种情况下可以同时在两个线程池线程中执行：一是计时器间隔小于执行该方法所需的时间；二是所有线程池线程都在使用，并且多次对该方法进行排队。
        /// </remarks>
        public TimeHelper(TimerCallback callback, Object state, TimeSpan dueTime, TimeSpan period)
        {
            _callback = callback;
            _state = state;
            _dueTime = dueTime;
            _period = period;
            Run(dueTime);
        }

        private TimerCallback _callback;
        private object _state;
        private TimeSpan _dueTime;
        private TimeSpan _period;
        private bool _isDisposing = false;
        private DateTime _NewDoCallbackTime;

        protected void Run(TimeSpan dueTime)
        {
            _NewDoCallbackTime = DateTime.Now.Add(dueTime);
            HttpRuntime.Cache.Insert(CacheID, this, null, _NewDoCallbackTime, Cache.NoSlidingExpiration, CacheItemPriority.NotRemovable, new CacheItemRemovedCallback(CacheItemRemovedCallback));

        }

        private void CacheItemRemovedCallback(String key, Object value, CacheItemRemovedReason reason)
        {
            TimeHelper timer = (TimeHelper)value;
            if (timer._isDisposing)
                return;
            if (reason == CacheItemRemovedReason.Expired)
            {
                if (timer._period < TimeSpan.FromSeconds(0))
                    _isDisposing = true;
                timer._callback(_state);
                timer.Run(timer._period);
            }
            else
            {
                timer.Run(_NewDoCallbackTime - DateTime.Now);
            }
        }

        /// <summary>
        /// 更改计时器的启动时间和方法调用之间的时间间隔，使用 TimeSpan 值度量时间间隔。 
        /// </summary>
        /// <param name="dueTime">一个 TimeSpan，表示在调用构造 Timer 时指定的回调方法之前的延迟时间量。指定负 -1 毫秒以防止计时器重新启动。指定零 (0) 以立即重新启动计时器。</param>
        /// <param name="period">在构造 Timer 时指定的回调方法调用之间的时间间隔。指定 -1 毫秒可以禁用定期终止。 </param>
        /// <returns></returns>
        public bool Change(TimeSpan dueTime, TimeSpan period)
        {
            this._dueTime = dueTime;
            this._period = period;
            try
            {
                HttpRuntime.Cache.Remove(CacheID);
                this.Run(this._dueTime);
                return true;
            }
            catch (Exception)
            {
                return false;
            }
        }

        private string _cacheID;
        protected string CacheID
        {
            get
            {
                if (_cacheID == null) _cacheID = GetHashCode().ToString();
                return _cacheID;
            }
        }

        #region IDisposable 成员

        public void Dispose()
        {
            _isDisposing = true;
        }



        #endregion 
    }
}